import React, { useEffect, useState, useCallback } from 'react'
import { connect } from 'react-redux'
import { NotifyType, RecordMode } from '../../common/enums'
import { getPlanSelector, getPlanActivePlanIdSelector } from '../../selectors/baseSelector'
import {
    genPlanChangeActivePlanField, genPlanSetActivePlan, genPlanAddPlan,
    genCommonForceReloadAllPlans, genSetActiveTab, genPlanClearAddedPlans
} from '../../actions/home'
import { getAllSuites } from '../../source/getData'
import { notify } from '../../source/notify'
import basicStyle from '../common/basic.scss'
import { Input } from '../common/Input'
import { EnvSelector } from '../common/envSelector'
import { PageSelector } from '../common/pageSelector'
import { SuiteLevelSelector } from '../common/suiteLevelSelector'
import { addPlan } from '../../source/saveData'
import { MultipleSelectableList } from '../layout/multipleSelectableList'
import { getStepListWithExtDataAsync } from '../../source/getStepListWithExtDataAsync'
import { genTempId } from '../../source/genTempId'
import { mergeStyle } from '../common/mergeStyle'
import classnames from 'classnames'
import style from './planEditor.scss'
import { runPlan } from '../../run/runPlan'
import { RequireFlag } from '../common/requireFlag'
import { ColHeader } from '../common/colHeader'
import { YOrNSelector } from '../common/yOrNSelector'
import { RetryTimesSelector } from '../common/retryTimesSelector'

const maStateToProps = state => {
    return {
        activePlan: getPlanSelector(state),
        activePlanId: getPlanActivePlanIdSelector(state)
    }
}

const mapDispatchToProps = dispatch => {
    return {
        changeActivePlanField: (filed, value) => dispatch(genPlanChangeActivePlanField(filed, value)),
        addPlanInStore: plan => dispatch(genPlanAddPlan(plan)),
        setPlanActivePlan: plan => dispatch(genPlanSetActivePlan(plan)),
        forceReloadAllPlans: () => dispatch(genCommonForceReloadAllPlans()),
        changeActiveTab: tab => dispatch(genSetActiveTab(tab)),
        clearAddedPlans: () => dispatch(genPlanClearAddedPlans())
    }
}

export const PlanEditor = connect(maStateToProps, mapDispatchToProps)((props) => {
    const { activePlan, activePlanId, changeActivePlanField, addPlanInStore, setPlanActivePlan,
        forceReloadAllPlans, changeActiveTab, clearAddedPlans } = props
    const { title, env, baseEnv, captureScreenShot, suiteIds, mode = '', retryTimesOnError } = activePlan
    const [loading, setLoading] = useState(true)
    const [allSuites, setAllSuites] = useState([])
    const [selectedSuiteIds, setSelectedSuiteIds] = useState(suiteIds)
    const [selectedLeftSuites, setSelectedLeftSuites] = useState([])
    const [selectedRightSuites, setSelectedRightSuites] = useState([])
    const [leftFilter, setLeftFitler] = useState({ title: '', page: '', level: '' })
    const [rightFilter, setRightFitler] = useState({ title: '', page: '', level: '' })
    const readOnly = mode !== RecordMode.add    // 不是新增模式就只读，不允许修改以前运行过的plan

    useEffect(() => {
        setLoading(true)
        getAllSuites()
            .then((data = []) => {
                setLoading(false)
                setAllSuites(data)
            }).catch(err => {
                setLoading(false)
                setAllSuites([])
                notify(NotifyType.error, err)
            })
    }, [])

    useEffect(() => {
        setSelectedLeftSuites([])
        setSelectedRightSuites([])
    }, [activePlanId])

    useEffect(() => setSelectedSuiteIds(suiteIds), [suiteIds])

    const onChangeTitle = useCallback(value => changeActivePlanField('title', value), [changeActivePlanField])
    const onChangeEnv = useCallback(item => changeActivePlanField('env', item), [changeActivePlanField])
    const onChangeBaseEnv = useCallback(item => changeActivePlanField('baseEnv', item), [changeActivePlanField])
    const onChangeCaptureScreenShot = useCallback(item => changeActivePlanField('captureScreenShot', item.code), [changeActivePlanField])
    const onChangeRetryTimesOnError = useCallback(item => changeActivePlanField('retryTimesOnError', item.code), [changeActivePlanField])

    const wrapOnClone = useCallback(() => {
        const toAdd = {
            ...activePlan,
            id: genTempId(),
            mode: RecordMode.add
        }

        addPlanInStore(toAdd)
        setPlanActivePlan(toAdd)
    }, [activePlan])

    const getSuiteTitle = useCallback(item => `[${item.page}:${item.level}]${item.title}`, [])
    const getExtItemTitleEl = useCallback(item => item.requireBaseEnv ? <span className={basicStyle.require} title='需要与基准环境运行结果比较'>*</span> : null, [])
    const getSuiteSelf = useCallback(item => item, [])
    const getSuiteId = useCallback(item => item.id, [])

    const genNewSource = (source, item, active) => {
        if (active) {
            return [...source, item]
        } else {
            const index = source.indexOf(item)
            if (index >= 0) {
                return [...source.slice(0, index), ...source.slice(index + 1)]
            } else {
                return [...source]
            }
        }
    }

    const onToggleLeftSuites = useCallback((item, newActive) => setSelectedLeftSuites(currentSuites => genNewSource(currentSuites, item, newActive)), [])
    const onToggleRightSuites = useCallback((item, newActive) => setSelectedRightSuites(currentSuites => genNewSource(currentSuites, item, newActive)), [])

    const onChangeFilter = useCallback((position, field, value) => {
        let filter = {
            left: setLeftFitler,
            right: setRightFitler
        }

        filter[position](currentFilter => ({ ...currentFilter, [field]: value }))
    }, [])

    const onChangeLeftTitle = useCallback(title => onChangeFilter('left', 'title', title), [])
    const onChangeLeftPage = useCallback(page => onChangeFilter('left', 'page', page), [])
    const onChangeLeftLevel = useCallback(level => onChangeFilter('left', 'level', level.code), [])

    const onChangeRightTitle = useCallback(title => onChangeFilter('right', 'title', title), [])
    const onChangeRightPage = useCallback(page => onChangeFilter('right', 'page', page), [])
    const onChangeRightLevel = useCallback(level => onChangeFilter('right', 'level', level.code), [])

    const ifSuiteIsSelected = (suite) => selectedSuiteIds.includes(suite.id)
    const ifMatchFilter = (suite, { title, page, level }) => (page
        ? suite.page === page : true)
        && (title ? suite.title.toLowerCase().indexOf(title.toLowerCase()) >= 0 : true)
        && (level ? suite.level === level : true)
        && (level ? suite.level === level : true)

    const allLeftSuites = allSuites.filter(suite => !ifSuiteIsSelected(suite) && ifMatchFilter(suite, leftFilter))      // 左侧显示的 suite：未被选择，且过滤
    const allRightSuites = allSuites.filter(suite => ifSuiteIsSelected(suite) && ifMatchFilter(suite, rightFilter))     // 右侧显示的 suite，已被选择，且过滤
    const allSelectedSuites = allSuites.filter(suite => ifSuiteIsSelected(suite))       // 已被选择，未过滤
    const requireBaseEnv = allSelectedSuites.some(suite => suite.requireBaseEnv)

    const moveAllToRight = useCallback(() => {
        setSelectedLeftSuites([])
        setSelectedSuiteIds(currentSelectedSuiteIds => currentSelectedSuiteIds.concat(allLeftSuites.map(suite => suite.id)))
    }, [allLeftSuites])

    const moveAllToLeft = useCallback(() => {
        setSelectedRightSuites([])
        setSelectedSuiteIds(currentSelectedSuiteIds => currentSelectedSuiteIds.filter(id => !allRightSuites.map(suite => suite.id).includes(id)))
    }, [allRightSuites])

    const moveSelectedToRight = useCallback(() => {
        setSelectedLeftSuites([])
        setSelectedSuiteIds(currentSelectedSuiteIds => currentSelectedSuiteIds.concat(selectedLeftSuites.map(suite => suite.id)))
    }, [selectedLeftSuites])

    const moveSelectedToLeft = useCallback(() => {
        setSelectedRightSuites([])
        setSelectedSuiteIds(currentSelectedSuiteIds => currentSelectedSuiteIds.filter(id => !selectedRightSuites.map(suite => suite.id).includes(id)))
    }, [selectedRightSuites])

    const quickMoveToRight = useCallback(suite => {
        if (!readOnly) {
            onToggleLeftSuites(suite, false)
            setSelectedSuiteIds(currentSelectedSuiteIds => currentSelectedSuiteIds.concat([suite.id]))
        }
    }, [onToggleLeftSuites, readOnly])

    const quickMoveToLeft = useCallback(suite => {
        if (!readOnly) {
            onToggleRightSuites(suite, false)
            setSelectedSuiteIds(currentSelectedSuiteIds => currentSelectedSuiteIds.filter(id => id !== suite.id))
        }
    }, [onToggleLeftSuites, readOnly])

    const validate = useCallback(() => {
        const errors = []

        selectedSuiteIds.length === 0 && errors.push('请点击右移按钮添加待执行suite')
        !title.trim() && errors.push('请输入Plan的标题');
        (!env || !env.name) && errors.push('请选择需要测试的目标环境');
        requireBaseEnv && (!baseEnv || !baseEnv.name) && errors.push('请选择基准验证环境')

        return errors
    }, [selectedSuiteIds, title, env, baseEnv, requireBaseEnv])

    const wrapOnSave = useCallback(async () => {
        const errors = validate()
        if (errors.length) {
            notify(NotifyType.error, errors)
            return
        }

        setLoading(true)

        const getSuiteDetailsPromiseList = selectedSuiteIds.map(suiteId => new Promise(async (resolve) => {
            const suite = allSuites.find(suite => suite.id === suiteId)
            const stepList = await getStepListWithExtDataAsync(suiteId)

            resolve({ ...suite, stepList })
        }))
        const allSuitesWithDetail = await Promise.all(getSuiteDetailsPromiseList)
        const plan = { title, env, baseEnv, captureScreenShot, retryTimesOnError, suites: allSuitesWithDetail }

        addPlan(plan)
            .then(async ({ data }) => {
                const planWithId = { ...plan, id: data.id }
                await runPlan(planWithId)

                setLoading(false)
                clearAddedPlans()                           //  清掉当前临时添加的plan
                changeActiveTab('progress')                 //  切tab
                forceReloadAllPlans()                       //  重新load plan下面的数据
                setPlanActivePlan({ ...planWithId, mode: '' })
                notify(NotifyType.info, '已启动运行')
            }).catch(err => {
                setLoading(false)
                notify(NotifyType.error, err)
            })
    }, [title, env, captureScreenShot, allSuites, selectedSuiteIds, validate, retryTimesOnError])

    const saveAvaliable = !readOnly
    const cloneAvaliable = readOnly && activePlanId

    return loading
        ? <div className={basicStyle.basic_loading} />
        : <div className={style.editor_root}>
            <ColHeader title='任务编辑器：' />
            <div className={classnames(basicStyle.actions, style.actions)}>
                {<button className={mergeStyle(basicStyle, classnames({ disabled: !saveAvaliable }))} onClick={saveAvaliable ? wrapOnSave : null}>保存并立即执行</button>}
                {<button className={mergeStyle(basicStyle, classnames({ disabled: !cloneAvaliable }))} onClick={readOnly && activePlanId ? wrapOnClone : null}>复制</button>}
            </div>
            <div className={style.header}>
                名称: <Input value={title} onChange={onChangeTitle} readOnly={readOnly} />
                <RequireFlag />

                目标测试环境: <EnvSelector placeholder='待测试环境' envName={env ? env.name || '' : ''} onSelectItem={onChangeEnv} readOnly={readOnly} />
                <RequireFlag title='选择待测试环境' />

                基准环境: <EnvSelector placeholder='基准环境' envName={baseEnv ? baseEnv.name || '' : ''} onSelectItem={onChangeBaseEnv} readOnly={readOnly} />
                {requireBaseEnv && <RequireFlag title='已选suite需要与基准环境运行结果比较' />}

                屏幕截图: <YOrNSelector inputClassName='width_80px' initValue={captureScreenShot}
                    onSelectItem={onChangeCaptureScreenShot} yText='是' nText='否' readOnly={readOnly} />

                出错重试: <RetryTimesSelector inputClassName='width_60px' initValue={retryTimesOnError}
                    onSelectItem={onChangeRetryTimesOnError} readOnly={readOnly} />
            </div>
            <div className={style.content}>
                <div className={style.left}>
                    <div className={style.filter}>
                        <Input className='width_130px' placeholder='筛选标题' value={leftFilter.title} onChange={onChangeLeftTitle} readOnly={readOnly} />
                        <PageSelector showNoLimitItem={true} initValue={leftFilter.page} onSelectItem={onChangeLeftPage} readOnly={readOnly} />
                        <SuiteLevelSelector showNoLimitItem={true} initValue={leftFilter.level} onSelectItem={onChangeLeftLevel} readOnly={readOnly} />
                    </div>
                    <MultipleSelectableList getItemKey={getSuiteId} activeValueList={selectedLeftSuites}
                        dataSource={allLeftSuites} getItemValue={getSuiteSelf} getItemTitle={getSuiteTitle}
                        getExtItemTitleEl={getExtItemTitleEl} onToggleSelectItem={onToggleLeftSuites}
                        onQuickAction={quickMoveToRight} readOnly={readOnly} />
                </div>
                <div className={`${mergeStyle(basicStyle, 'actions')} ${mergeStyle(style, 'middle')}`}>
                    <button className={mergeStyle(basicStyle, classnames({ disabled: readOnly || !allLeftSuites.length }))} onClick={readOnly ? null : moveAllToRight}>&gt;&gt;</button>
                    <button className={mergeStyle(basicStyle, classnames({ disabled: readOnly || !allRightSuites.length }))} onClick={readOnly ? null : moveAllToLeft}>&lt;&lt;</button>
                    <button className={mergeStyle(basicStyle, classnames({ disabled: readOnly || !allLeftSuites.length || !selectedLeftSuites.length }))} onClick={readOnly ? null : moveSelectedToRight}>&gt;</button>
                    <button className={mergeStyle(basicStyle, classnames({ disabled: readOnly || !allRightSuites.length || !selectedRightSuites.length }))} onClick={readOnly ? null : moveSelectedToLeft}>&lt;</button>
                </div>
                <div className={style.right}>
                    <div className={style.filter}>
                        <Input className='width_130px' placeholder='筛选标题' value={rightFilter.title} onChange={onChangeRightTitle} readOnly={readOnly} />
                        <PageSelector showNoLimitItem={true} initValue={rightFilter.page} onSelectItem={onChangeRightPage} readOnly={readOnly} />
                        <SuiteLevelSelector showNoLimitItem={true} initValue={rightFilter.level} onSelectItem={onChangeRightLevel} readOnly={readOnly} />
                    </div>
                    <MultipleSelectableList getItemKey={getSuiteId} activeValueList={selectedRightSuites}
                        dataSource={allRightSuites} getItemValue={getSuiteSelf} getItemTitle={getSuiteTitle}
                        getExtItemTitleEl={getExtItemTitleEl} onToggleSelectItem={onToggleRightSuites}
                        onQuickAction={quickMoveToLeft} readOnly={readOnly} />
                </div>
            </div>
        </div>
})